<template>
  <div class="C-lazySelect-container">
    <el-select
      id="lazy-select"
      v-model="valueCopy"
      v-bind="$attrs"
      v-on="$listeners"
      v-el-select-loadmore="handleLoadmore"
      class="select-lazy"
      filterable
      :remote="remote"
      :remote-method="handleAttrValueFilter"
      :multiple="multiple"
      clearable
    >
      <el-option
        v-for="(item, index) in optionList"
        :key="index"
        :label="item[optionsAll.optionLableKey]"
        :value="item[[optionsAll.optionValueKey]]"
      >
      </el-option>
      <div v-if="!selectEnd" class="loading-box" v-loading="selectLoading" element-loading-text="加载中..."></div>
      <div class="bottom-text" v-if="selectEnd">到底啦~</div>
    </el-select>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    value: {
      type: [String, Boolean, Array],
      default: ''
    },
    remote: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    api: {
      type: Function,
      default: function () {}
    },
    detailsApi: {
      type: Function,
      default: function () {}
    },
    options: {
      type: Object,
      default() {
        return {}
      }
    }
  },
  components: {},
  computed: {
    valueCopy: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
      }
    },
    optionsAll() {
      return Object.assign({}, this.defaultOptions, this.options)
    }
  },
  watch: {
    options: {
      handler(a, b) {
        this.searchParams = {
          [this.optionsAll.searchKey]: '',
          [this.optionsAll.pageKey]: this.optionsAll.defaultPage,
          [this.optionsAll.pageSizeKey]: this.optionsAll.defaultPageSize
        }
        // 初次加载配置
        if (!b) {
          this.searchParams[this.optionsAll.pageKey] = 1
          this.optionList = []
          this.getList()
        }
      },
      immediate: true,
      deep: true
    },
    valueCopy: {
      async handler(newVal, oldVal) {
        if (this.multiple) {
          this.multipleDataEcho(newVal, oldVal)
        } else {
          this.dataEcho(newVal, oldVal)
        }
      },
      immediate: true
    }
  },
  data() {
    return {
      optionList: [],
      defaultOptions: {
        searchKey: 'name', //查询键名
        pageKey: 'currentPage', //当前页码键名
        pageSizeKey: 'pageSize', //查询条数键名
        defaultPage: 1, //默认当前页码
        defaultPageSize: 10, //默认查询条数
        optionLableKey: 'label', //label键名字
        optionValueKey: 'value', //value键名
        queryDetailsKey: 'id', //查询详情接口的key
        resultKey: '' //结果
      },
      searchParams: {},
      selectLoading: false,
      selectEnd: false,
      unmatchedData: null
    }
  },
  methods: {
    // 下拉加载
    handleLoadmore() {
      if (!this.selectLoading && !this.selectEnd) {
        this.searchParams[this.optionsAll.pageKey]++
        this.getList()
      }
    },
    // 搜索
    handleAttrValueFilter(name) {
      if (!this.selectLoading) {
        this.selectEnd = false
        this.searchParams[this.optionsAll.searchKey] = name
        this.searchParams[this.optionsAll.pageKey] = 1
        this.optionList = []
        this.getList()
      }
    },
    // 获取列表
    getList() {
      this.selectLoading = true
      this.api(this.searchParams)
        .then((res) => {
          let list = this.optionsAll.resultKey ? res[this.optionsAll.resultKey] : res
          if (!Array.isArray(list) || !list.length) return (this.selectEnd = true)
          this.dataExclusion(list)
          this.optionList = this.optionList.concat(list)
        })
        .finally(async () => {
          await this.$nextTick()
          this.selectLoading = false
        })
    },
    // 单选首次数据回显
    async dataEcho(newVal, oldVal) {
      if (!!newVal && !oldVal) {
        let details = await this.detailsApi({ [this.optionsAll.queryDetailsKey]: newVal })
        let exist = this.computeExist(newVal, this.optionList)
        if (Object.prototype.toString.call(details) === '[object Object]' && !exist) {
          this.optionList.unshift(details)
          this.unmatchedData = details
        } else if (Object.prototype.toString.call(details) === '[object Array]' && !exist) {
          this.optionList = details.concat(this.optionList)
          this.unmatchedData = details
        }
      }
    },
    // 多选首次数据回显
    async multipleDataEcho(newVal, oldVal) {
      if (!!newVal && newVal.length && (!oldVal || !oldVal.length)) {
        let details = await this.detailsApi({ [this.optionsAll.queryDetailsKey]: newVal.toString() })
        if (Object.prototype.toString.call(details) === '[object Object]') {
          details = [details]
        }
        if (!details || !details.length) return
        let unmatchedData = []
        for (let valItem of newVal) {
          let exist = this.computeExist(newVal, this.optionList)
          if (exist) continue
          let detailsItem = details.find((n) => n[this.optionsAll.optionValueKey] === valItem)
          if (!detailsItem) continue
          unmatchedData.push(detailsItem)
          this.optionList.unshift(detailsItem)
        }
        this.unmatchedData = []
      }
    },
    // 回显数据排除
    dataExclusion(list) {
      // 多选
      if (
        Object.prototype.toString.call(this.unmatchedData) === '[object Array]' &&
        this.unmatchedData.length &&
        this.multiple
      ) {
        for (let unmatchedDataItem of this.unmatchedData) {
          let exist = this.computeExist(unmatchedDataItem[this.optionsAll.optionValueKey], list)
          if (exist) continue
          list = list.filter(
            (n) => n[this.optionsAll.optionValueKey] !== unmatchedDataItem[this.optionsAll.optionValueKey]
          )
          this.unmatchedData = this.unmatchedData.filter(
            (n) => n[this.optionsAll.optionValueKey] !== unmatchedDataItem[this.optionsAll.optionValueKey]
          )
        }
      }
      // 单选
      else if (Object.prototype.toString.call(this.unmatchedData) === '[object Object]') {
        let exist = this.computeExist(this.unmatchedData[this.optionsAll.optionValueKey], list)
        if (exist) {
          list = list.filter(
            (n) => n[this.optionsAll.optionValueKey] !== this.unmatchedData[this.optionsAll.optionValueKey]
          )
          this.unmatchedData = null
        }
      }
    },
    computeExist(val, list) {
      let exist = false
      if (!list.length) return exist
      for (let item of list) {
        if (item[this.optionsAll.optionValueKey] === val) {
          exist = true
          break
        }
      }
      return exist
    }
  },
  directives: {
    'el-select-loadmore': {
      bind(el, binding) {
        const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
        SELECTWRAP_DOM.addEventListener('scroll', function () {
          const condition = this.scrollHeight - this.scrollTop.toFixed(0) <= this.clientHeight
          if (condition) {
            binding.value()
          }
        })
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import '@/styles/variables.scss';
.loading-box {
  height: 30px;
  ::v-deep .el-loading-spinner {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 30px;
    .circular {
      width: 20px;
      height: 20px;
    }
  }
}
.bottom-text {
  height: 30px;
  line-height: 30px;
  text-align: center;
  color: #409eff;
}
// .C-lazySelect-container {
// }
</style>
